package com.example.muchentuner.metro;

import static android.content.Context.MODE_PRIVATE;

import static com.example.muchentuner.ButtonActivity.PREFS_FILE;

import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraManager;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Build;
import android.os.Bundle;
import android.os.Vibrator;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import android.widget.Button;

import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

import com.example.muchentuner.ButtonActivity;
import com.example.muchentuner.KeyView;
import com.example.muchentuner.MainActivity;
import com.example.muchentuner.R;
import com.suke.widget.SwitchButton;

import android.content.SharedPreferences;
import android.widget.SeekBar;
import android.widget.Toast;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;

import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelProviders;



public class HomeFragment extends BaseFragment{

    final int MIN_TEMPO = 40;
    final int MAX_TEMPO = 400;

    private SoundPool soundPool;

    int tempo, beat, subdiv;
    SoundNum sNum = new SoundNum(1);

    boolean enabled;
    long[] taps;

    Tick tick;
    Button metro_btn;
    SeekBar seekBar;
    private MyViewModel myViewModel;

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        metro_btn = (Button) getActivity().findViewById(R.id.metro_btn);
        metro_btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                toggle();
            }
        });

        final SharedPreferences sp = getActivity().getSharedPreferences(PREFS_FILE,
                MODE_PRIVATE);//如果发现fragment里，一些东西用不了，试试getActivity
        final SharedPreferences.Editor editor = sp.edit();
        Button metro_minus = (Button) getActivity().findViewById(R.id.metro_minus);
        metro_minus.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                editor.putInt("tempo",tempo-1);
                editor.apply();
                tempo = sp.getInt("tempo",100);
                MetroView.show_tempo=tempo;
                seekBar.setProgress(tempo);
                intermediateTempoChange();
            }
        });
        Button metro_plus = (Button) getActivity().findViewById(R.id.metro_plus);
        metro_plus.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                editor.putInt("tempo",tempo+1);
                editor.apply();
                tempo = sp.getInt("tempo",100);
                MetroView.show_tempo=tempo;
                seekBar.setProgress(tempo);
                intermediateTempoChange();
            }
        });

        seekBar = (SeekBar) getActivity().findViewById(R.id.seekBar);
        seekBar.setProgress(tempo);

        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                //将seekBar的进度设置在textView上
                editor.putInt("tempo",progress);
                editor.apply();
                tempo=progress;
                MetroView.show_tempo=tempo;
                intermediateTempoChange();
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
            }
        });

        final SharedPreferences sp_beat = getActivity().getSharedPreferences(PREFS_FILE,
                MODE_PRIVATE);//如果发现fragment里，一些东西用不了，试试getActivity
        final SharedPreferences.Editor editor_beat = sp_beat.edit();
        Button beat_minus = (Button) getActivity().findViewById(R.id.beat_minus);
        beat_minus.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                editor_beat.putInt("beat",beat-1);
                editor_beat.apply();
                beat = sp_beat.getInt("beat",4);
                MetroView.show_beats=beat;
                intermediateBeatsChange();
            }
        });
        Button beat_plus = (Button) getActivity().findViewById(R.id.beat_plus);
        beat_plus.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                editor_beat.putInt("beat",beat+1);
                editor_beat.apply();
                beat = sp_beat.getInt("beat",4);
                MetroView.show_beats=beat;
                intermediateBeatsChange();
            }
        });

        final SharedPreferences sp_sub = getActivity().getSharedPreferences(PREFS_FILE,
                MODE_PRIVATE);//如果发现fragment里，一些东西用不了，试试getActivity
        final SharedPreferences.Editor editor_sub = sp_sub.edit();
        Button sub_minus = (Button) getActivity().findViewById(R.id.sub_minus);
        sub_minus.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                subdiv = sp_sub.getInt("subdiv",1);
                if(subdiv>1)
                {
                    editor_sub.putInt("subdiv",subdiv-1);
                    editor_sub.apply();
                    subdiv = sp_sub.getInt("subdiv",1);
                    MetroView.show_subdiv=(int)Math.pow(2,subdiv+1);
                    intermediateSubChange();
                }
            }
        });
        Button sub_plus = (Button) getActivity().findViewById(R.id.sub_plus);
        sub_plus.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                subdiv = sp_sub.getInt("subdiv",1);
                if(subdiv<6){
                    editor_sub.putInt("subdiv",subdiv+1);
                    editor_sub.apply();
                    subdiv = sp_sub.getInt("subdiv",1);
                    MetroView.show_subdiv=(int)Math.pow(2,subdiv+1);
                    intermediateSubChange();
                }
            }
        });

        myViewModel = new ViewModelProvider(
                requireActivity(),
                new ViewModelProvider.NewInstanceFactory()).get(MyViewModel.class);

        myViewModel.getSelectMusic().observe(getActivity(), new Observer<SoundNum>() {
            @Override public void onChanged(SoundNum soundNum){
                intermediateSoundNumChange(soundNum.getSoundNum());
                sNum.setSoundNum(myViewModel.getSoundNum(soundNum));
            }
        });

        sbListener();
    }

    private void sbListener() {
        com.suke.widget.SwitchButton Ref_Pitch_Button = (com.suke.widget.SwitchButton)
                getActivity().findViewById(R.id.metro_sb);
        final SharedPreferences btn_state_pref = getActivity().getSharedPreferences(PREFS_FILE, MODE_PRIVATE);
        swiBtn_state = btn_state_pref.getBoolean("metro_switch_btn", true);
        Ref_Pitch_Button.setChecked(swiBtn_state);//设置为真，即默认为真
        //Ref_Pitch_Button.isChecked();//被选中

        Ref_Pitch_Button.toggle();     //开关状态
        Ref_Pitch_Button.toggle(true);//开关有动画
        Ref_Pitch_Button.setShadowEffect(false);//禁用阴影效果
        Ref_Pitch_Button.setEnabled(true);//false为禁用按钮
        Ref_Pitch_Button.setEnableEffect(true);//false为禁用开关动画
        Ref_Pitch_Button.setOnCheckedChangeListener(new SwitchButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(SwitchButton view, boolean isChecked) {
                final SharedPreferences preferences = getActivity().getSharedPreferences(PREFS_FILE,
                        MODE_PRIVATE);
                if (Ref_Pitch_Button.isChecked()) {
                    SharedPreferences.Editor editor = preferences.edit();
                    editor.putBoolean("metro_pointer", true);
                    editor.apply();

                    SharedPreferences.Editor editor2 = preferences.edit();
                    editor2.putBoolean("metro_switch_btn", true);
                    editor2.apply();
                } else {
                    SharedPreferences.Editor editor = preferences.edit();
                    editor.putBoolean("metro_pointer", false);
                    editor.apply();

                    SharedPreferences.Editor editor2 = preferences.edit();
                    editor2.putBoolean("metro_switch_btn", false);
                    editor2.apply();
                }
                pointer=preferences.getBoolean("metro_pointer",true);
            }
        });
    }

    @Override
    protected void initViews() {
        enabled = false;
        set_SoundPool();

        final SharedPreferences sp = getActivity().getSharedPreferences(PREFS_FILE,
                MODE_PRIVATE);//如果发现fragment里，一些东西用不了，试试getActivity
        tempo = sp.getInt("tempo",100);
        beat = sp.getInt("beat",4);
        subdiv = sp.getInt("subdiv",1);

        MetroView.show_tempo=tempo;
        MetroView.show_beats=beat;
        MetroView.show_subdiv=subdiv*4;

        final SharedPreferences btn_state_pref = getActivity().getSharedPreferences(PREFS_FILE, MODE_PRIVATE);
        swiBtn_state = btn_state_pref.getBoolean("metro_switch_btn", true);
        pointer=btn_state_pref.getBoolean("metro_pointer",true);

        MetroView metroView = find(R.id.metro_view);
        metroView.invalidate();
    }

    @Override
    protected int getLayoutId(){
        return R.layout.homefrm;
    }

    private int soundId1;
    private int soundId1p;
    private int soundId2;
    private int soundId2p;
    private int soundId3;
    private int soundId3p;
    private int soundId4;
    private int soundId4p;
    private int soundId5;
    private int soundId5p;
    private int soundId6;
    private int soundId6p;
    private int soundId7;
    private int soundId7p;
    private int soundId;
    private int soundId_p;

    public void set_SoundPool(){
        AudioAttributes audioAttributes = null;
        audioAttributes = new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build();

        soundPool = new SoundPool.Builder()
                .setMaxStreams(8)
                .setAudioAttributes(audioAttributes)
                .build();

        soundId1=soundPool.load(getContext(), R.raw.metro_normal, 1); // 加载音频资源
        soundId1p=soundPool.load(getContext(), R.raw.metro_point, 1);
        soundId2=soundPool.load(getContext(), R.raw.synthe,1);
        soundId2p=soundPool.load(getContext(), R.raw.synthe_p,1);
        soundId3=soundPool.load(getContext(), R.raw.elec,1);
        soundId3p=soundPool.load(getContext(), R.raw.elec_p,1);
        soundId4=soundPool.load(getContext(), R.raw.drum,1);
        soundId4p=soundPool.load(getContext(), R.raw.drum_p,1);
        soundId5=soundPool.load(getContext(), R.raw.maracas,1);
        soundId5p=soundPool.load(getContext(), R.raw.maracas_p,1);
        soundId6=soundPool.load(getContext(), R.raw.temple_block,1);
        soundId6p=soundPool.load(getContext(), R.raw.temple_block_p,1);
        soundId7=soundPool.load(getContext(), R.raw.hand,1);
        soundId7p=soundPool.load(getContext(), R.raw.hand_p,1);
    }

    private void intermediateTempoChange() {
        if (!enabled) return;
        long elapsedTime = System.currentTimeMillis() - tick.tickTime(tick.nTicks - 1);

        tick.tempo = tempo;
        if (elapsedTime >= tick.delayTime()) {
            // immediate tick
            tick.nTicks = 0;
            tick.startTime = System.currentTimeMillis();
            tick.nextTime = tick.startTime;
        } else {
            // count the time since the last tick towards the next one
            tick.nTicks = 1;
            tick.startTime = System.currentTimeMillis() - elapsedTime;
            tick.nextTime = tick.tickTime(1);
        }

        // break out of any sleeps currently happening
        tick.interrupt();
    }

    private void intermediateBeatsChange() {
        if (!enabled) return;
        long elapsedTime = System.currentTimeMillis() - tick.tickTime(tick.nTicks - 1);

        tick.beats = beat;
        if (elapsedTime >= tick.delayTime()) {
            // immediate tick
            tick.nTicks = 0;
            tick.startTime = System.currentTimeMillis();
            tick.nextTime = tick.startTime;
        } else {
            // count the time since the last tick towards the next one
            tick.nTicks = 1;
            tick.startTime = System.currentTimeMillis() - elapsedTime;
            tick.nextTime = tick.tickTime(1);
        }

        // break out of any sleeps currently happening
        tick.interrupt();
    }

    private void intermediateSubChange() {
        if (!enabled) return;
        long elapsedTime = System.currentTimeMillis() - tick.tickTime(tick.nTicks - 1);

        tick.subdiv = subdiv;
        if (elapsedTime >= tick.delayTime()) {
            // immediate tick
            tick.nTicks = 0;
            tick.startTime = System.currentTimeMillis();
            tick.nextTime = tick.startTime;
        } else {
            // count the time since the last tick towards the next one
            tick.nTicks = 1;
            tick.startTime = System.currentTimeMillis() - elapsedTime;
            tick.nextTime = tick.tickTime(1);
        }

        // break out of any sleeps currently happening
        tick.interrupt();
    }

    public void intermediateSoundNumChange(int soundNum) {
        if (!enabled) return;
        long elapsedTime = System.currentTimeMillis() - tick.tickTime(tick.nTicks - 1);

        tick.soundNum = soundNum;
        sNum.soundNum = soundNum;

        if (elapsedTime >= tick.delayTime()) {
            // immediate tick
            tick.nTicks = 0;
            tick.startTime = System.currentTimeMillis();
            tick.nextTime = tick.startTime;
        } else {
            // count the time since the last tick towards the next one
            tick.nTicks = 1;
            tick.startTime = System.currentTimeMillis() - elapsedTime;
            tick.nextTime = tick.tickTime(1);
        }

        // break out of any sleeps currently happening
        tick.interrupt();
    }


    private void toggle() {
        enabled = !enabled;
        if (enabled) {
            metro_btn.setBackground(getResources().getDrawable(R.drawable.ic_close_btn));
            //activeDot = -1;
            tick = new Tick(tempo, beat,subdiv,sNum.getSoundNum());
            tick.start();
        } else {
            metro_btn.setBackground(getResources().getDrawable(R.drawable.ic_open_btn));
            if (tick != null) {
                tick.keepGoing = false;
                tick.interrupt();
            }
        }
    }

    private int angle=50;
    private boolean swiBtn_state;
    private boolean pointer;
    class Tick extends Thread {
        protected int tempo, subdiv, nTicks,beats,soundNum;
        protected long startTime, nextTime;
        boolean keepGoing;
        long detime;
        boolean pendl;
        public Tick(int tempo, int beats,int subdiv,int soundNum) {
            this.tempo = tempo;
            this.beats = beats;
            this.subdiv = subdiv;
            this.soundNum = soundNum;
            keepGoing = true;
        }

        @Override public void run() {
            nTicks = 0;
            startTime = System.currentTimeMillis();
            nextTime = startTime;
            pendl=true;
            while (keepGoing) {
                FlashUtils utils = new FlashUtils(getContext());

                long diff = nextTime - System.currentTimeMillis();
                detime=diff;
                if (getActivity() != null) getActivity().runOnUiThread(new Runnable() {
                    @Override public void run() {
                        anim();
                    }
                });
                if (diff <= 0) {}
                // else if (diff <= 5) {
                //     // 5ms - arbitrary cutoff for when to busyloop
                //     while (System.currentTimeMillis() < nextTime);
                // }
                else {
                    // we have a while - sleep and check again
                    try { Thread.sleep(diff); } catch (InterruptedException e) {}
                    continue;
                }

                if (getActivity() == null) toggle();

                switch (soundNum){
                    case 1:
                        soundId_p=soundId1p;
                        soundId=soundId1;
                        break;
                    case 2:
                        soundId_p=soundId2p;
                        soundId=soundId2;
                        break;
                    case 3:
                        soundId_p=soundId3p;
                        soundId=soundId3;
                        break;
                    case 4:
                        soundId_p=soundId4p;
                        soundId=soundId4;
                        break;
                    case 5:
                        soundId_p=soundId5p;
                        soundId=soundId5;
                        break;
                    case 6:
                        soundId_p=soundId6p;
                        soundId=soundId6;
                        break;
                    case 7:
                        soundId_p=soundId7p;
                        soundId=soundId7;
                        break;
                }
                // time for another tick
                ++nTicks;
                if(((nTicks-1)%beats==0)&&pointer)
                {
                    soundPool.play(soundId_p,1f,1f,1,0,1);
                }

                soundPool.play(soundId,1f,1f,1,0,1);

                if (ThirdFragment.Lighting){
                    utils.open();//打开手电筒
                }
                if(ThirdFragment.Vibrate){
                    startVibrate(getActivity(), new long[]{0,100},false);
                }

                pendl=!pendl;

                try { Thread.sleep(Math.min(100, (long)(delayTime()/2))); } catch (InterruptedException e) {}

                // queue the next tick
                nextTime = tickTime(nTicks);

                utils.close();//关闭手电筒
            }
        }

        protected long tickTime(int nTick) {
            return startTime + Math.round(nTick * delayTime());
        }

        protected double delayTime() { return 1000 * 60.0 / (tempo*subdiv); }

        private void anim(){
            //<动画部分
            if (pendl)
            {
                ValueAnimator anim = ValueAnimator.ofFloat(0f,-angle,0f);
                if(tick.detime>0)
                {
                    anim.setDuration(tick.detime+100);
                    anim.setInterpolator(new MyInterpolator());
                    anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                        @Override
                        public void onAnimationUpdate(ValueAnimator animation) {
                            float currentValue = (float) animation.getAnimatedValue();
                            MetroView.current_angle=currentValue;
                        }
                    });
                    anim.start();
                    //动画部分>
                }
            }
            else {
                ValueAnimator anim = ValueAnimator.ofFloat(0f, angle,0f);
                if(tick.detime>0){
                    anim.setDuration(tick.detime+100);
                    anim.setInterpolator(new MyInterpolator());
                    anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                        @Override
                        public void onAnimationUpdate(ValueAnimator animation) {
                            float currentValue = (float) animation.getAnimatedValue();
                            MetroView.current_angle=currentValue;
                        }
                    });
                    anim.start();
                    //动画部分>
                }
            }
        }


    }


    public class FlashUtils {
        private CameraManager manager;
        private Camera mCamera = null;
        private Context context;
        private boolean status = false;//记录手电筒状态

        FlashUtils(Context context){
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
            }
            this.context = context;
        }

        //打开手电筒
        public void open() {
            if(status){//如果已经是打开状态，不需要打开
                return;
            }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                try {
                    manager.setTorchMode("0", true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                PackageManager packageManager = context.getPackageManager();
                FeatureInfo[] features = packageManager.getSystemAvailableFeatures();
                for (FeatureInfo featureInfo : features) {
                    if (PackageManager.FEATURE_CAMERA_FLASH.equals(featureInfo.name)) { // 判断设备是否支持闪光灯
                        if (null == mCamera) {
                            mCamera = Camera.open();
                        }
                        Camera.Parameters parameters = mCamera.getParameters();
                        parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
                        mCamera.setParameters(parameters);
                        mCamera.startPreview();
                    }
                }
            }
            status = true;//记录手电筒状态为打开
        }

        //关闭手电筒
        public void close() {
            if(!status){//如果已经是关闭状态，不需要打开
                return;
            }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                try {
                    manager.setTorchMode("0", false);
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
            } else {
                if (mCamera != null) {
                    mCamera.stopPreview();
                    mCamera.release();
                    mCamera = null;
                }
            }
            status = false;//记录手电筒状态为关闭
        }

        //改变手电筒状态
        public void converse(){
            if(status){
                close();
            }else{
                open();
            }
        }
    }

    public static void startVibrate(final Activity activity, long[] pattern, boolean isRepeat){
            Vibrator vib = (Vibrator) activity.getSystemService(Service.VIBRATOR_SERVICE);
            vib.vibrate(pattern, isRepeat ? 1 : -1);
    }
    public static void stopVibrate(final Activity activity) {
        Vibrator vib = (Vibrator) activity.getSystemService(Service.VIBRATOR_SERVICE);
        vib.cancel();
    }
}

class SoundNum implements Serializable {
    public int soundNum;

    public SoundNum(int soundNum) {
        this.soundNum = soundNum;
    }

    public int getSoundNum(){
        return soundNum;
    }
    public void setSoundNum(int num){
        this.soundNum = num;
    }
}